package com.android.readcard.utils.djlm.newDJMLReader;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

import java.io.IOException;
import java.util.UUID;

/**
 * Created by Admin on 2016/9/14.
 */
public class DJLM_BLEService {
    private final static String TAG = "蓝牙";

    private BluetoothManager mBluetoothManager;
    public String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    public int mConnectionState = STATE_DISCONNECTED;

    public static final int STATE_DISCONNECTED = 0;
    public static final int STATE_CONNECTING = 1;
    public static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.nordicsemi.nrfUART.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.nordicsemi.nrfUART.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.nordicsemi.nrfUART.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.nordicsemi.nrfUART.ACTION_DATA_AVAILABLE";
    public final static String ACTION_DATA_SEND =
            "com.nordicsemi.nrfUART.ACTION_DATA_SEND";
    public final static String EXTRA_DATA =
            "com.nordicsemi.nrfUART.EXTRA_DATA";
    public final static String EXTRA_DATA2 =
            "com.nordicsemi.nrfUART.EXTRA_DATA2";
    public final static String DEVICE_DOES_NOT_SUPPORT_UART =
            "com.nordicsemi.nrfUART.DEVICE_DOES_NOT_SUPPORT_UART";
    public static final UUID CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
    public static final UUID RX_SERVICE_UUID = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb");
    public static final UUID TX_CHAR_UUID = UUID.fromString("0000fff4-0000-1000-8000-00805f9b34fb");

    public static final UUID RX_CHAR6_UUID = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb");

    public static final UUID RX_CHAR7_UUID = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb");

    public Object m_ConnectEvent = new Object();
    Context m_ctx = null;

    DJLM_BLEService(Context ctx) {
        m_ctx = ctx;
    }

    public static DJLM_BLEService g_DJLM_BLEService = null;

    public static DJLM_BLEService GetBLEService(Context ctx) {
        if (g_DJLM_BLEService == null) {
            g_DJLM_BLEService = new DJLM_BLEService(ctx);
        }
        return g_DJLM_BLEService;
    }

    // Implements callback methods for GATT events that the app cares about.  For example,
    // connection change and services discovered.
    private final BLECallback mGattCallback = new BLECallback();

    class BLECallback extends BluetoothGattCallback {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            String intentAction;

            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction, gatt.getDevice().getName());
                Log.e(TAG, "Connected to GATT server.");
                // Attempts to discover services after successful connection.
                Log.e(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());
                synchronized (m_ConnectEvent) {
                    m_ConnectEvent.notifyAll();
                }
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.e(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction, gatt.getDevice().getName());
                synchronized (m_ConnectEvent) {
                    m_ConnectEvent.notifyAll();
                }
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            SetLastNotification(characteristic.getValue());
            //gatt.beginReliableWrite();
            //Log.e(TAG, "onCharacteristicChanged");
            //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                enableTXNotification();
                //	Log.e(TAG, "mBluetoothGatt = " + mBluetoothGatt);
                //broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.e(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            //Log.e(TAG, "onCharacteristicWrite" + "(" + status + ")");
            if (status != BluetoothGatt.GATT_SUCCESS) {
                Log.e(TAG, "onCharacteristicWrite" + "(" + status + ")");
                disconnect();
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        }
    }

    ;

    private void broadcastUpdate(final String action) {
        //   final Intent intent = new Intent(action);
        //LocalBroadcastManager.getInstance(m_ctx).sendBroadcast(intent);
    }

    private void broadcastUpdate(final String action, String strData) {
        //   final Intent intent = new Intent(action);
        //  intent.putExtra(EXTRA_DATA, strData);
        //   LocalBroadcastManager.getInstance(m_ctx).sendBroadcast(intent);
    }


    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {

        // final Intent intent = new Intent(action);

        // This is special handling for the Heart Rate Measurement profile.  Data parsing is
        // carried out as per profile specifications:
        // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
        //   if (TX_CHAR_UUID.equals(characteristic.getUuid())) {
        //    // Log.e(TAG, String.format("Received TX: %d",characteristic.getValue() ));
        //intent.putExtra(EXTRA_DATA, characteristic.getValue());
        // } else {

        // }
        //   LocalBroadcastManager.getInstance(m_ctx).sendBroadcast(intent);
    }

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) m_ctx.getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }
        return true;
    }

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     * @return Return true if the connection is initiated successfully. The connection result
     * is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    public boolean connect(final String address, BluetoothDevice device) {
        m_RxService = null;
        if (address == null) {
            Log.e(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.e(TAG, "Trying to use an existing mBluetoothGatt for connection." + mBluetoothDeviceAddress);
            if (mConnectionState != STATE_CONNECTED) {
                if (mBluetoothGatt.connect()) {
                    mConnectionState = STATE_CONNECTING;
                    Log.e(TAG, "use an existing mBluetoothGatt for connection succeed.");
                    return true;
                } else {
                    Log.e(TAG, "use an existing mBluetoothGatt for connection fail.");
                    disconnect();
                    return false;
                }
            } else {
                broadcastUpdate(ACTION_GATT_CONNECTED, mBluetoothGatt.getDevice().getName());
                return true;
            }
        }
        if (device == null) {
            Log.e(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        mBluetoothGatt = device.connectGatt(m_ctx, false, mGattCallback);
        Log.e(TAG, "Trying to create a new connection." + address);
        if (mBluetoothGatt != null) {
            mBluetoothDeviceAddress = address;
            mConnectionState = STATE_CONNECTING;
            Log.e(TAG, "create a new connection succeed.");
            return true;
        } else
            Log.e(TAG, "create a new connection fail.");
        return false;
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The disconnection result
     * is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    public void disconnect() {
        if (mBluetoothGatt == null) {
            Log.e(TAG, "BluetoothAdapter not initialized1");
            return;
        }
        mBluetoothGatt.disconnect();
        Log.e(TAG, "BluetoothAdapter not initialized2");
//        mBluetoothGatt = null;
        m_RxService = null;
        //mBluetoothGatt.close();
    }

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        Log.e(TAG, "mBluetoothGatt closed");
        //mBluetoothDeviceAddress = null;
        //mBluetoothGatt.close();
        //mBluetoothGatt = null;
    }

    /**
     * Enable TXNotification
     *
     * @return
     */
    public void enableTXNotification() {

        if (mBluetoothGatt == null) {
            disconnect();
            showMessage("mBluetoothGatt null" + mBluetoothGatt);
            broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
            return;
        }

        BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
        if (RxService == null) {
            disconnect();
            showMessage("Rx service not found!");
            broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
            return;
        }
        BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(TX_CHAR_UUID);
        if (TxChar == null) {
            disconnect();
            showMessage("Tx charateristic not found!");
            broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
            return;
        }
        mBluetoothGatt.setCharacteristicNotification(TxChar, true);

        BluetoothGattDescriptor descriptor = TxChar.getDescriptor(CCCD);
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }

    BluetoothGattService m_RxService = null;

    private BluetoothGattService GetRxService() {
        if (m_RxService != null) {
            return m_RxService;
        }
        if (mBluetoothGatt == null) {
            m_RxService = null;
            return null;
        }

        if (m_RxService == null)
            m_RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
        return m_RxService;
    }

    BluetoothGattCharacteristic m_RX_CHAR6 = null;

    private BluetoothGattCharacteristic GetCHAR6() {
        if (m_RX_CHAR6 != null) {
            return m_RX_CHAR6;
        }
        BluetoothGattService RxService = GetRxService();
        if (RxService == null) {
            m_RX_CHAR6 = null;
            return null;
        }
        if (m_RX_CHAR6 == null) {
            m_RX_CHAR6 = RxService.getCharacteristic(RX_CHAR6_UUID);
        }
        return m_RX_CHAR6;
    }

    BluetoothGattCharacteristic m_RX_CHAR7 = null;

    private BluetoothGattCharacteristic GetCHAR7() {
        if (m_RX_CHAR7 != null) {
            return m_RX_CHAR7;
        }
        BluetoothGattService RxService = GetRxService();
        if (RxService == null) {
            Log.e(TAG, "DJLM_BLEService: RxService这个东西NULL");
            m_RX_CHAR7 = null;
            return null;
        }
        if (m_RX_CHAR7 == null) {
            m_RX_CHAR7 = RxService.getCharacteristic(RX_CHAR7_UUID);
        }
        return m_RX_CHAR7;
    }

    public boolean writeRXCharacteristic(byte[] value) throws IOException {
        if (mBluetoothGatt == null) {
            throw new IOException("BluetoothGatt is null!");
        }

        BluetoothGattCharacteristic RxChar = GetCHAR6();
        if (RxChar == null) {
            disconnect();
            showMessage("Rx charateristic not found!");
            //broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
            throw new IOException("Rx charateristic not found!");
            //return false;
        }
        if (mConnectionState != STATE_CONNECTED){
            Log.i(TAG, "writeRXCharacteristic: " + mConnectionState);
            throw new IOException("BLE Disconnected!");
        }
        RxChar.setValue(value);
        boolean status = mBluetoothGatt.writeCharacteristic(RxChar);
        //  showMessage("writeCharacteristic" + "(" + status + ")");
        return status;
    }

    public boolean writeRXCharacteristic2(byte[] value) throws IOException {
        if (mBluetoothGatt == null)
            throw new IOException("BluetoothGatt is null!");
        BluetoothGattCharacteristic RxChar = GetCHAR7();

        if (RxChar == null) {
            disconnect();
            showMessage("Rx charateristic not found!");
            //broadcastUpdate(DEVICE_DOES_NOT_SUPPORT_UART);
            throw new IOException("Rx charateristic not found!");
            //return false;
        }
        if (mConnectionState != STATE_CONNECTED)
            throw new IOException("Disconnected!");
        RxChar.setValue(value);
        boolean status = mBluetoothGatt.writeCharacteristic(RxChar);
        //   showMessage("writeCharacteristic2" + "(" + status + ")");
        return status;
    }

    private void showMessage(String msg) {
        Log.e(TAG, msg);
    }

    private byte[] m_lastNotification = null;
    private boolean mSilence = false;

    void OnNotification(byte[] notification) {
//            if (notification != null) {
//                if(mSilence)
//                {
//                    String str = null;
//                    if (notification.length > 0)
//                        str = "r 81" + HexUtil.bytesToHexString(notification);
//                    else
//                        str = "r 84";
//                    final Intent intent = new Intent(ACTION_DATA_AVAILABLE);
//                    intent.putExtra(EXTRA_DATA, str.getBytes());
//                    LocalBroadcastManager.getInstance(m_ctx).sendBroadcast(intent);
//                }
//
//            }
    }

    public void SetSilence(boolean bSilence) {
        synchronized (this) {
            mSilence = bSilence;
        }
    }

    void SetLastNotification(byte[] notification) {
        synchronized (this) {
            m_lastNotification = notification.clone();
            notifyAll();
            // if (!mSilence)
            //    OnNotification(notification);
        }
    }

    public boolean WaiteBLEConnnect(int nTimeout, int nDelayTime) {
        if (mConnectionState == STATE_CONNECTED) {
            return true;
        }

        synchronized (m_ConnectEvent) {
            if (nTimeout > 0) {
                try {
                    m_ConnectEvent.wait(nTimeout);
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
        }
        if (mConnectionState == STATE_CONNECTED) {
            if (nDelayTime > 0) {
                try {
                    Thread.sleep(nDelayTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return true;
        } else {
            return false;
        }
    }

    public void WaiteBLEConnnect(int nTimeout, int nDelayTime, Handler handler) {
        new Thread(new WaiteBLEConnnectRunnable(nTimeout, nDelayTime, handler)).run();
    }

    class WaiteBLEConnnectRunnable implements Runnable {
        int m_nTimeout;
        int m_nDelayTime;
        Handler m_nHandler;

        WaiteBLEConnnectRunnable(int nTimeout, int nDelayTime, Handler handler) {
            m_nTimeout = nTimeout;
            m_nDelayTime = nDelayTime;
            m_nHandler = handler;
            Log.i(TAG, "WaiteBLEConnnectRunnable:已经启动了");
        }

        @Override
        public void run() {
            if (WaiteBLEConnnect(m_nTimeout, 0)) {
                m_nHandler.sendEmptyMessageDelayed(1, m_nDelayTime);
            } else {
                // 蓝牙连接失败
                m_nHandler.sendEmptyMessageDelayed(0, m_nDelayTime);
            }
        }
    }

    public byte[] GetLastNotification(int nTimeout) throws IOException {
        byte[] ret = null;
        synchronized (this) {
            if (nTimeout > 0) {
                try {
                    wait(nTimeout);
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
            if (m_lastNotification != null) {
                ret = m_lastNotification;
                m_lastNotification = null;
            }
        }
        return ret;
    }
}
